﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Net;
using WLive.WLiveObjects;

namespace WLive
{
    public class Requester
    {
        public static int bufferSize = 8192;

        /// <summary>
        /// 
        /// </summary>
        /// <typeparam name="T">Object which have to be return</typeparam>
        /// <param name="requestObject">Object with request data</param>
        /// <returns>Returns collection of objects gives from JSON</returns>
        public static IEnumerable<T> Request<T>(RequestObject requestObject)
        {
            return Request<T>(requestObject, null);
        }
        /// <summary>
        /// 
        /// </summary>
        /// <typeparam name="T">Type of object which you want return</typeparam>
        /// <param name="requestObject">Object with request data</param>
        /// <param name="handler">Handler for upload request</param>
        /// <returns>Return collection of object which gives from JSON</returns>
        public static IEnumerable<T> Request<T>(RequestObject requestObject, RequestProgressHandler handler)
        {
            List<T> obj = new List<T>();
            bool error = false;
            string response_content_type="";
            MemoryStream ms = new MemoryStream();
            WebException exception = null;
            try
            {
                HttpWebRequest request = (HttpWebRequest)WebRequest.Create(requestObject.url);
                request.Method = requestObject.method;
                
                request.Headers = requestObject.GetHeaders();
                request.ContentLength = requestObject.content_length;
                request.ContentType = !string.IsNullOrEmpty(requestObject.content_type) ? requestObject.content_type : request.ContentType;
                if (requestObject.content_length > 0)
                {
                    using (Stream s = request.GetRequestStream())
                    {
                        int k = 0;
                        while (k < requestObject.Data.Length)
                        {
                            if (k + bufferSize < requestObject.Data.Length)
                            {
                                s.Write(requestObject.Data, k, bufferSize);
                                k += bufferSize;
                                if (handler != null && handler.ProgressChange != null)
                                    if (requestObject is BitsRequest)
                                        handler.ProgressChange(
                                            ((((BitsRequest)requestObject).count-1) * ((BitsRequest)requestObject).MaxLenght) + k, (double)(((((BitsRequest)requestObject).count-1) * ((BitsRequest)requestObject).MaxLenght) + k) / (((BitsRequest)requestObject).total_length / (double)100));
                                    else
                                        handler.ProgressChange(k, (k / (request.ContentLength / 100)));
                            }
                            else
                            {
                                s.Write(requestObject.Data, k, requestObject.Data.Length - k);
                                k += requestObject.Data.Length - k;
                                if (handler != null && handler.ProgressChange != null)
                                    if (requestObject is BitsRequest)
                                        handler.ProgressChange(
                                            ((((BitsRequest)requestObject).count-1) * ((BitsRequest)requestObject).MaxLenght) + k, (double)(((((BitsRequest)requestObject).count-1) * ((BitsRequest)requestObject).MaxLenght) + k) / (((BitsRequest)requestObject).total_length / (double)100));
                                    else
                                        handler.ProgressChange(k, (k / (request.ContentLength / 100)));
                            }
                        }
                    }
                }
                HttpWebResponse response = (HttpWebResponse)request.GetResponse();
                response_content_type = response.ContentType;
                if (requestObject is BitsRequest)
                    ((BitsRequest)requestObject).SetSessionId(response.Headers["BITS-Session-Id"]);
                response.GetResponseStream().CopyTo(ms);
            }
            catch (WebException e)
            {
                exception = e;
                error = true;
                if (e.Response != null)
                {
                    HttpWebResponse response = (HttpWebResponse)e.Response;
                    response_content_type = response.ContentType;
                    response.GetResponseStream().CopyTo(ms);
                }
            }
            finally
            {
                ms.Position = 0;
                StreamReader sr = new StreamReader(ms);
                string s = sr.ReadToEnd();
                if (error)
                {
                    if (response_content_type.Contains("application/json"))
                    {
                        if (s.Contains("\"code\":"))
                        {
                            ErrorObj err = Newtonsoft.Json.JsonConvert.DeserializeObject<ErrorObj>(s);
                            throw new WLiveException(err);
                        }
                        else
                        {
                            Error err = Newtonsoft.Json.JsonConvert.DeserializeObject<Error>(s);
                            throw new WLiveException(err);
                        }
                    }
                    throw new Exception(exception.Message, exception);
                }
                else
                {
                    if (response_content_type.Contains("application/json"))
                    {
                        if (s.Contains("data"))
                        {
                            DataObject<T> data = Newtonsoft.Json.JsonConvert.DeserializeObject<DataObject<T>>(s);
                            obj.AddRange(data.data);
                        }
                        else
                        {
                            obj.Add(Newtonsoft.Json.JsonConvert.DeserializeObject<T>(s));
                        }
                    }
                }
            }
            return obj;
        }
    }

    public class RequestObject
    {
        byte[] data;
        WebHeaderCollection hcol = new WebHeaderCollection();
        public string url;
        public string method = "GET";
        public int content_length = 0;
        public string content_type = "";
        public byte[] Data
        {
            get { return data; }
            set
            {
                data = value;
                content_length = data.Length;
            }
        }
        /// <summary>
        /// 
        /// </summary>
        /// <returns>Returns headers for request</returns>
        public virtual WebHeaderCollection GetHeaders()
        {
            return hcol;
        }
        /// <summary>
        /// Set string data into byte array
        /// </summary>
        /// <param name="stringdata">Data for request stream</param>
        public void SetData(string stringdata)
        {
            Data = System.Text.Encoding.UTF8.GetBytes(stringdata);
        }
        /// <summary>
        /// Add header into collection
        /// </summary>
        /// <param name="header">Header name</param>
        /// <param name="value">Header value</param>
        public void AddHeader(string header, string value)
        {

            hcol.Add(header, value);
        }
    }
    public class BitsRequest: RequestObject
    {
        int state;
        int maxLenght = -1;
        string session_id;
        
        public int total_length;
        public int count;
        
        public int MaxLenght {
            get { return maxLenght; }
        }

        public BitsRequest()
        {
            state = 0;
            method = "POST";
            maxLenght = Utils.Settings.GetSettingsInt("OneDrive.UploadChunkedSize", 16777216);
        }
        /// <summary>
        /// 
        /// </summary>
        /// <returns>Returns header based on state of bits request</returns>
        public override WebHeaderCollection GetHeaders()
        {
            switch (state)
            {
                case 0: return GetHeadersSessionStart();
                case 1: return GetHeadersUpload();
                case 2: return GetHeaderCloseSession();
            }
            return null;
        }
        /// <summary>
        /// Set session id of upload
        /// </summary>
        /// <param name="session">Upload session id</param>
        public void SetSessionId(string session)
        {
            session_id = session;
        }
        /// <summary>
        /// Method initiate new session
        /// </summary>
        public void StartSession()
        {
            state = 0;
        }
        /// <summary>
        /// Method for initiate commit session
        /// </summary>
        public void CommitSession()
        {
            state = 2;
        }
        /// <summary>
        /// Method prepare headers for start session
        /// </summary>
        /// <returns>Returns headers collection for start session</returns>
        protected WebHeaderCollection GetHeadersSessionStart()
        {
            state = 1;
            WebHeaderCollection hrc = new WebHeaderCollection();
            hrc.Add("X-Http-Method-Override", "BITS_POST");
            hrc.Add("Authorization", "Bearer " + Connection.Token);
            hrc.Add("BITS-Packet-Type", "Create-Session");
            hrc.Add("BITS-Supported-Protocols", "{7df0354d-249b-430f-820d-3d2a9bef4931}");
            return hrc;
        }
        /// <summary>
        /// Method prepare headers for upload fragment
        /// </summary>
        /// <returns>Returns header collection for fragment upload</returns>
        protected WebHeaderCollection GetHeadersUpload()
        {
            int cl = content_length;
            WebHeaderCollection hrc = new WebHeaderCollection();
            hrc.Add("X-Http-Method-Override", "BITS_POST");
            hrc.Add("Authorization", "Bearer " + Connection.Token);
            hrc.Add("BITS-Packet-Type", "Fragment");
            hrc.Add("BITS-Session-Id", session_id);
            hrc.Add("Content-Range", "bytes "+(MaxLenght * count).ToString() + "-" + ((MaxLenght * count) + (cl - 1)).ToString() + "/" + total_length);
            count++;
            return hrc;
        }
        /// <summary>
        /// Method prepare headers for close session
        /// </summary>
        /// <returns>Returns header collection for closing session</returns>
        protected WebHeaderCollection GetHeaderCloseSession()
        {
            content_length = 0;
            WebHeaderCollection hrc = new WebHeaderCollection();
            hrc.Add("X-Http-Method-Override", "BITS_POST");
            hrc.Add("Authorization", "Bearer " + Connection.Token);
            hrc.Add("BITS-Packet-Type", "Close-Session");
            hrc.Add("BITS-Session-Id", session_id);
            return hrc;
        }
    }

    public class RequestProgressHandler
    {
        public delegate void OnProgressChange(int bytes, double percent);
        public OnProgressChange ProgressChange;
    }
}
